#include "lib.h"

#include "bjgame.h"
#include "bjbet.h"
#include "bjconn.h"
#include "bjhand.h"
#include "bjsaved.h"
#include "bjrules.h"
#include <ctype.h>
#include "cardset.h"


//
// balance is included in EVERY message
//

BJBet::BJBet(BJGame* game, BJConnection* conn, BJRules* pRules, 
			BJHand* dealer, BJHand* player, int32 bet, int8 iBJBet)
{
    m_dealerEnded       = false;
    m_iBJBet            = iBJBet;
	this->game			= game;
	this->conn			= conn;
	this->player		= player;
	this->dealer		= dealer;
	m_pRules			= pRules;
	m_bet				= bet;

	current				= player;
	currentHand			= 0;
	splitCount			= 0;
	m_insuranceState	= NO;
	m_insurance			= 0;

    strcpy(gid, "-1");
}


BJBet::BJBet(BJGame* game, BJConnection* conn, BJRules* pRules, 
			BJHand* dealer, BJHand* player, BJSavedGame* sg, int8 i)
{
    m_dealerEnded       = false;

    BJSavedGame::_BJBet*  sBet = &(sg->BJBets[i]);
    strcpy(gid, sBet->gid);

    m_iBJBet            = sBet->iBJBet;
    this->game			= game;
	this->conn			= conn;
	this->player		= player;
	this->dealer		= dealer;
	m_pRules			= pRules;
	m_bet               = sBet->bet;
	splitCount          = sBet->numPlayerHands - 1;
	current				= player;   // this may be changed from BJGame !!
    currentHand         = 0;        // this may be changed from BJGame !!

	RenumberHands();

	m_insuranceState = (InsuranceState)(sBet->m_insuranceState);

	if( m_insuranceState==TAKEN || m_insuranceState==WON || m_insuranceState==LOST)
	{
		m_insurance = m_bet/2;  
	}
}

BJBet::~BJBet()
{
	BJHand* temp;

	while (player)
	{
		temp = player;
		player = (BJHand*)player->getNextHand();
		delete temp;
	}

}

void
BJBet::SetCurrentHandOffset( int32 cH )
{
    BJHand* probe;
    int32 i=0;

    currentHand = cH;
    for(probe=player; probe; i++)
	{
        if( i == currentHand )
		{
            current = probe;
			break;
		}
		probe = (BJHand*)probe->getNextHand();
	}
}


uint8
BJBet::GetPlayerOptions()
{
	return  (uint8)m_pRules->GetPlayerOptions( current, splitCount+1, dealer );
}


bool8
BJBet::HandlePlayerNextAction(int32 balance)
{
	int actionBits = GetPlayerOptions();

	if( m_insuranceState == OFFERED )
	{
		//normal insurance - first hand - no auto action
		if (!game->IsLateInsurance())
			return true;

		//late insurance - last hand - no auto stand
		if (!(current->getNextHand()))
			if (actionBits == (1<<Stand))
				return true;
	}

	switch (actionBits)
	{
	case (1<<Hit):
		return HandlePlayerHit(balance);
		break;
	case (1<<Stand):
		return HandlePlayerStand();
		break;
	case (1<<Double):
		return HandlePlayerDouble(balance);
		break;
	case (1<<Split):
		return HandlePlayerDouble(balance);
		break;
	case (1<<Surrender):
		return HandlePlayerDouble(balance);
		break;
	/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
	case (1<<Zap):
		return HandlePlayerZap(balance);
		break;

	default:
		break;
	}

	return true;
}

bool8
BJBet::PlayerDeal()
{
	//must have a hand
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerBet( current, splitCount ))
		return false;

	current->setAction(BJF_BET);
	return true;
}


bool8
BJBet::HandlePlayerHit(int32 balance)
{
	//must have a hand
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerHit( current, splitCount+1 ))
		return false;

	game->DealCardD(current);

	current->setAction(BJF_HIT);


    if (conn->trackPlayerHand( gid, current, false, &balance))
	{
		//
		//	<msgsize>H<new card>
		//
		conn->msgStart();
		conn->SENDBYTE('H');
		conn->SENDINT4(balance);
		current->sendLastCard( conn );
		conn->SENDBYTE( (uint8)current->GetScoreVisible() );
		conn->msgEnd();

		return game->HandlePlayerNextAction(balance);
	}
	else
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();

		return false;
	}
}

bool8
BJBet::HandlePlayerStand()		// or bust
{
	int32 balance;

	//must have a hand
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerStand( current, splitCount+1 ))
		return false;

	current->setAction(BJF_STAND);

	conn->msgStart();

	int isBusted = 	m_pRules->IsPlayerHandBusted( current);
	uint8 tag = isBusted ? 's' : 'S';

    int8   nextBet = -1;

	if( ! current->getNextHand() )
    {
        // this game (BJBet) has finished (no next hand available )
        // before we save game state, we need to establish whether
        // another BJBet follows (currentBJBet should point to the next BJBet) 
	    pBJBet temp = game->nextBJBet();
	    
        if( temp )
	        nextBet = temp->GetBetOffset();

    }
    
    if (conn->trackPlayerHand( gid, current, false, &balance, nextBet ))
	{

		conn->SENDBYTE(tag);
		conn->SENDINT4(balance);
		conn->msgEnd();

	    current = (BJHand*)current->getNextHand();
	    ++currentHand;

	    if (current)
		    return game->HandlePlayerNextAction(balance);

		//no hands left
        game->HandleDealer();  

		return true;
	}
	else
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();
		return false;
	}
}

bool8
BJBet::HandlePlayerDouble(int32 balance)
{
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerDouble( current, splitCount+1 ))
		return false;

	game->DealCardD( current );

	current->setAction(BJF_DOUBLE);
	current->SetNBets( current->GetNBets() * 2 );

	if (conn->trackPlayerHand( gid, current, true, &balance ))
	{
		//when not saving fun play data deduct the double down amount
		//when double down is taken
		if(! conn->shouldSaveGameData())
			balance = conn->trackAdjustBalance(-GetBet() * (current->GetNBets()/2));

		conn->msgStart();
		conn->SENDBYTE('B');
		conn->SENDINT4(balance);
		current->sendLastCard( conn );
		conn->SENDBYTE( (uint8)current->GetScoreVisible() );
		conn->msgEnd();

		return game->HandlePlayerNextAction(balance);
	}
	else
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();
		return false;
	}
}

void
BJBet::RenumberHands()
{
	BJHand* pHand = player;

	for (int i=0; pHand; i++)
	{
		pHand->SetNHand(i);
		pHand = (BJHand*)pHand->getNextHand();
	}
}


bool8
BJBet::HandlePlayerSplit(int32 balance)
{
	//must have a hand
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerSplit( current, splitCount+1 ))
		return false;

	BJHand* next = current->HandleSplit();

	if (!next)
		return false;	// todo: server error

	++splitCount;

	//hand numbers have changed ... so we need to renumber them
	RenumberHands();

	if (conn->trackPlayerHands(gid,player, splitCount))
	{
		//when not saving fun play data, go ahead and deduct the
		//bet from the split when the split is made
		if(! conn->shouldSaveGameData())
			balance = conn->trackAdjustBalance(-GetBet());

		conn->msgStart();
		conn->SENDBYTE('P');
		conn->SENDINT4(balance);
		game->sendState(false);    // kharmon. 02-27-07.  #16101.  
		conn->msgEnd();

		return game->HandlePlayerNextAction(balance);
	}
	else
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();
		return false;
	}

}

void
BJBet::HandlePlayerPayout()
{
	int32 balance;
	//check for unresolved bet
	if (current->GetNBets() == 0)
		return;

	//what is the entire wager on this hand
	int wager	= GetCurrentBet();

	//what is the amount to be added/subtracted to this hand
	char* pPlayerBonusName=NULL;
	char* pDealerBonusName=NULL;

	int payout	= m_pRules->GetPayout
	(
		wager,
		current,
		dealer,
		splitCount+1,
		&pPlayerBonusName,
		&pDealerBonusName
	);

	if (pDealerBonusName != NULL)
		dealer->SetBonusName( pDealerBonusName );
	if (pPlayerBonusName != NULL)
		current->SetBonusName( pPlayerBonusName );

	//the amount above and beyond the original bet
	int bonus = payout - wager;
	if (bonus > 0)
		current->SetBonus( bonus );

	//amount of money awarded to the player if any
	wager += payout;

	//tell the hand the payout result
	current->SetPayout( payout );

	//record the net win for the player and return result to player balance
	// the exception is 'Surrender" that has already been handled
	// in "HandlePlayerSurrender"
	if( ! current->wasAction(BJF_SURRENDER) )
		if (!conn->trackPayout(gid, current, wager ))
		{
			// On a SQL error, return an error back to the client
			conn->msgStart();
			conn->SENDBYTE('E');
			conn->msgEnd();
			return;
		}

	//and save the player hand to be sure
	if (!conn->trackPlayerHand( gid, current, false, &balance ))
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();
		return;
	}
}

bool8
BJBet::HandlePlayerSurrender(int32 balance)
{
	//must have a hand
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerSurrender( current, splitCount+1 ))
		return false;

	current->setAction(BJF_SURRENDER);

	// get the surrender payout 
	// this function (with "BJF_SURRENDER" set) will need first 2 parameters only
	int wager	= GetCurrentBet();
	int payout = m_pRules->GetPayout( wager,current,dealer,0, (char**)0, (char**)0 );
	wager += payout;

	//record the surrender for the player and return result to player balance
	if (!conn->trackPayout(gid, current, wager ))
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();
		return false;
	}

	if (conn->trackPlayerHand(gid, current, false, &balance))
	{
		conn->msgStart();
		conn->SENDBYTE('R');
		conn->SENDINT4(balance);
		conn->SENDINT4( payout ); //payout
		conn->msgEnd();

		return game->HandlePlayerNextAction(balance);
	}
	else
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();
		return false;
	}
}

/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
bool8 BJBet::HandlePlayerZap(int32 balance)
{
	//must have a hand
	if (!current)
		return false;

	//must be within the rules
	if (!m_pRules->CanPlayerZap( current, splitCount+1 ))
		return false;

	// Change previous card to new 2 cards
	current->HandleZap();
	game->DealCardD(current);
	game->DealCardD(current);

	current->setAction(BJF_ZAP);

	if (conn->trackPlayerHand( gid, current, false, &balance))
	{
		conn->msgStart();
		conn->SENDBYTE('Z');
		conn->SENDINT4(balance);
		for (int i=0; i<2; i++) {
			current->sendCard( i, conn );
		}
		conn->SENDBYTE((uint8)current->GetScoreVisible());
		conn->msgEnd();

		return game->HandlePlayerNextAction(balance);
	}
	else
	{
		// On a SQL error, return an error back to the client
		conn->msgStart();
		conn->SENDBYTE('E');
		conn->msgEnd();

		return false;
	}
}


void
BJBet::sendState()
{
	BJHand*	probe;
	uint8*	countNdx;
	int32	numHands;

	//
	// This message is contained within another message
	// (don't call msgStart()/msgEnd()).
	//

	//index of this bet/game
	conn->SENDBYTE(m_iBJBet);

	//bet
	conn->SENDINT4(m_bet);

    //insurance
	conn->SENDBYTE(m_insuranceState);

	//current player hand
	conn->SENDBYTE(currentHand & 0xFF);

	//num hand records 
	countNdx = conn->msgGetBuf();	// reserve for num hands
	conn->msgReleaseBuf(1);

    numHands = 0;

	//player hands
	for(probe=player; probe; numHands++)
	{
		probe->send( conn );
		probe = (BJHand*)probe->getNextHand();
	}

	*countNdx = (uint8)(numHands & 0xFF);
}

int32
BJBet::GetCurrentBet()
{
	if (!current)
		return 0;

	int nBets = current->GetNBets();

	return m_bet * nBets;
}

void
BJBet::sendPayouts()
{
    conn->SENDBYTE(m_iBJBet);
    
    //nHands
	int32	numHands = 0;
	uint8*	numHandIndex;

    numHandIndex = conn->msgGetBuf();	// reserve for num hands
    conn->msgReleaseBuf(1);

	// Calc payouts...note that the server has already subtracted
	// bets, doubles, splits, etc from the account; so, the balanceAdjust
	// (returned only to the server) must include ties
	//
	// Individual hand payouts (returned to the client) are encoded as follows:
	//		player wins:	+amount won
	//		player ties:	0
	//		player loses:	-amount lost
	//

	for (current = player; current; current = (BJHand*)current->getNextHand() )
	{
		HandlePlayerPayout();

		int32 payout = current->GetPayout();
		int32 bonus  = current->GetBonus();
		char* pBonusName = current->GetBonusName();

		//send the hand payout result
		conn->SENDINT4( payout ); //payout
		conn->SENDINT4( bonus  ); //bonus
		conn->SENDNSTRING( pBonusName ); //no name

		numHands++;
	}

	*numHandIndex = (uint8)(numHands); 
}

bool8
BJBet::EndOfTurnPlayer()
{
	bool8 isEOG = m_pRules->EndOfTurnPlayer( player, dealer, splitCount+1 );
	return isEOG;
}

